#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "cachectrl.h"

#include "bus.h"


__u8 CCacheCtrl::read_byte (__u32 address)
{
	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 dat;


	// check cache 
	//if(!enabled())
	//{
	//	dat=bus_read_byte(address);
	//	return dat;
	//}


	// if L1 hit
	if(L1_D_Cache.read_byte(address,&dat))
	{
		return dat;
	}

	// if L2  miss
	if(!L2_Cache.read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer))
	{
		read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer);
	}


	// fill line L1
	if(L1_D_Cache.fill_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{

		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer);
	}

	return buffer[address&&((1<<N_L2_LEN_IN_BITS)-1)];
}
__u16 CCacheCtrl::read_short(__u32 address)
{
	__u16 dat;

	// check cache 
	//if(!enabled())
	//{
	//	dat=bus_read_short(address);
	//	return dat;
	//}

	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];

	// if L1 hit
	if(L1_D_Cache.read_short(address,&dat))
	{
		return dat;
	}

	// if L2  miss
	if(!L2_Cache.read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer))
	{
		read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer);
	}


	// fill line L1
	if(L1_D_Cache.fill_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{

		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer);
	}

	dat=*(__u16*)& buffer[address&((1<<N_L2_LEN_IN_BITS)-1)];
	
	return dat;
}
__u32 CCacheCtrl::read_int(__u32 address)
{
	__u32 dat;

	// check cache 
	//if(!enabled())
	//{
	//	dat=bus_read_int(address);
	//	return dat;
	//}

	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];

	// if L1 hit
	if(L1_D_Cache.read_int(address,&dat))
	{
		return dat;
	}

	// if L2  miss
	if(!L2_Cache.read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer))
	{
		read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer);
	}


	// fill line L1
	if(L1_D_Cache.fill_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{

		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer);
	}

	dat=*(__u32*)& buffer[address&&((1<<N_L2_LEN_IN_BITS)-1)];
	
	return dat;
}

void CCacheCtrl::write_byte (__u32 address,__u8  dat)
{
	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];
	// if L1 swap out
	if(!L1_D_Cache.write_byte(address,dat,buffer))
	{
		return ;
	}
	// if L2 swap out, go to write buffer
	if(L2_Cache.write_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{
		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer) ;
	}
	
}
void CCacheCtrl::write_short(__u32 address,__u16 dat)
{

	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];
	// if L1 swap out
	if(!L1_D_Cache.write_short(address,dat,buffer))
	{
		return ;
	}
	// if L2 swap out, go to write buffer
	if(L2_Cache.write_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{
		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer) ;
	}
	
}
void CCacheCtrl::write_int(__u32 address,__u32 dat)
{

	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];
	// if L1 swap out
	if(!L1_D_Cache.write_int(address,dat,buffer))
	{
		return ;
	}
	// if L2 swap out, go to write buffer
	if(L2_Cache.write_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{
		pushto_wr_buffer(address&~((1<<N_L2_LEN_IN_BITS)-1),tmp_buffer) ;
	}

}

__u32 CCacheCtrl::icache_read_int(__u32 address)
{
	__u8 buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u8 tmp_buffer[1<<N_L1_CACHELINES_IN_BITS];
	__u32 dat;

	// if L1 hit
	if(L1_I_Cache.read_int(address,&dat))
	{
		return dat;
	}

	// if L2  miss
	if(!L2_Cache.read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer))
	{
		read_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer);
	}


	// fill line L1, I cache won't be dirty
	if(L1_I_Cache.fill_line(address&~((1<<N_L2_LEN_IN_BITS)-1),buffer,tmp_buffer))
	{
		//pushto_wr_buffer(address,tmp_buffer);
	}

	return *(__u32 *)&buffer[address&&((1<<N_L2_LEN_IN_BITS)-1)];
}

void CCacheCtrl::write_l2_line(__u32 address,__u8 * data)
{
}
void CCacheCtrl::write_l1_line(__u32 address,__u8 * data)
{
}

//
// for performance analyse,N_WR_BUFF is variable.
// so algorithm was not optimized.
//

int  CCacheCtrl::get_wr_buf_no()
{
	int i,j;
	// if full, write out the oldest entry
	if(stack_top==N_WR_BUFF-1)
	{
		i=index_stack[0];

		for( j=1;j<N_WR_BUFF;j++)
            index_stack[j-1]=index_stack[j];


		// write back
		bus_write_line(write_buffer[i].address,write_buffer[i].data);
	}
	else
	{
		for(i=0;i<N_WR_BUFF;i++)
		{
			for(j=0;j<stack_top;j++)
				if(i==index_stack[j]) break;

			if(j==stack_top) break;
		}
		// pigeon nest thoery.
		assert(i<N_WR_BUFF);

		stack_top++;
	}
	index_stack[j]=i;

	return i;

}
void  CCacheCtrl::read_line(__u32 address,__u8 * p_data)
{

	assert((address&0x1f)==0);
	// scan write buffer

	int i;
	for(i=0;i<stack_top;i++)
	{
		if(write_buffer[index_stack[i]].address==address) 
			break;
	}


	// if found.
	if(i<stack_top)
	{
		memcpy(p_data,write_buffer[index_stack[i]].data,32);
		// claim back the space

		
		do
		{
			i++;
			index_stack[i-1]=index_stack[i];
		}while(i<stack_top);
		stack_top--;

	}
	else
	//
	bus_read_line(address,p_data);
}

void CCacheCtrl::pushto_wr_buffer(__u32 address,__u8 * p_data)
{
		int i=get_wr_buf_no();

		write_buffer[i].address=address&~0x1f;
		memcpy(write_buffer[i].data,p_data,32);
}